/**
 * 2025/01/01 - 2025/12/31
 */
package com.snail.starter.iam.authentication.local.token;


import cn.hutool.crypto.digest.DigestUtil;
import com.snail.starter.commons.threadlocal.UserContext;
import com.snail.starter.commons.enums.HeaderEnums;
import com.snail.starter.commons.error.CommonErrorCode;
import com.snail.starter.commons.exception.BizException;
import com.snail.starter.iam.authentication.jwt.JwtService;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * @author zgadmin
 * @date 2025/7/2 13:21
 */
public abstract class AbstractTokenService implements ITokenService {
    //token 失效时间
    public static final int TOKEN_EXPIRE = 60;

    @Resource
    protected JwtService jwtService;

    public abstract boolean checkMaxSession(String loginName, int maxSession);

    public abstract void incrementSession(String loginName, String token);

    public abstract void decrementSession(String loginName, String token);

    public abstract void put(String token, String tokenEncode);

    public abstract void delete(String loginName, String token);

    public abstract void deleteAll(String loginName);

    public abstract UserContext getUserContext(String token);

    @Override
    public String generate(UserContext userContext) {
        String tokenEncode = jwtService.encode(userContext);
        String token = DigestUtil.md5Hex(userContext.getUserName() + tokenEncode);
        if (-1 != userContext.getMaxSession() && !checkMaxSession(userContext.getUserName(), userContext.getMaxSession())) {
            throw BizException.of(CommonErrorCode.EXCEED_MAX_SESSION);
        }
        put(token, tokenEncode);
        incrementSession(userContext.getUserName(), token);
        addCookieAndSession(token);
        return token;
    }

    @Override
    public boolean renew(String token) {
        UserContext cahcheUserContext = get(token);
        addCookieAndSession(token);
        return null != cahcheUserContext;
    }

    @Override
    public boolean destroy(String token) {
        UserContext context = get(token);
        delete(context.getUserName(), context.getToken());
        delCookieAndSession();
        return true;
    }

    @Override
    public boolean destroyAll(String token) {
        UserContext context = get(token);
        deleteAll(context.getUserName());
        return true;
    }

    @Override
    public UserContext get(String token) {
        UserContext userContext = getUserContext(token);
        addCookieAndSession(token);
        return userContext;
    }

    private void addCookieAndSession(String token) {
        ServletRequestAttributes servletRequestAttributes = getServletRequestAttributes();
        if (null == servletRequestAttributes) {
            return;
        }
        HttpServletRequest request = servletRequestAttributes.getRequest();
        if (null == request) {
            return;
        }
        HttpSession session = request.getSession();
        if (null != session) {
            session.setAttribute(HeaderEnums.TOKEN.getKey(), token);
            session.setMaxInactiveInterval(TOKEN_EXPIRE * 60);
        }
        HttpServletResponse response = servletRequestAttributes.getResponse();
        if (null == response) {
            return;
        }
        Cookie cookie = new Cookie(HeaderEnums.TOKEN.getKey(), token);
        cookie.setPath("/");
        cookie.setMaxAge(TOKEN_EXPIRE * 60);
        cookie.setHttpOnly(true);
        response.addCookie(cookie);
    }

    private void delCookieAndSession() {
        ServletRequestAttributes servletRequestAttributes = getServletRequestAttributes();
        if (null == servletRequestAttributes) {
            return;
        }
        HttpServletRequest request = servletRequestAttributes.getRequest();
        if (null == request) {
            return;
        }
        HttpSession session = request.getSession(false);
        if (null != session) {
            session.removeAttribute(HeaderEnums.TOKEN.getKey());
        }
        HttpServletResponse response = servletRequestAttributes.getResponse();
        if (null == response) {
            return;
        }
        Cookie cookie = new Cookie(HeaderEnums.TOKEN.getKey(), null);
        cookie.setPath("/");
        cookie.setMaxAge(0);
        response.addCookie(cookie);
    }

    private ServletRequestAttributes getServletRequestAttributes() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (null == requestAttributes || !(requestAttributes instanceof ServletRequestAttributes)) {
            return null;
        }
        return (ServletRequestAttributes) requestAttributes;
    }
}